/*******************************************************************************************
 *  BulletMLParser.c
 *
 *  parseBulletMLFile : returns a pointer on a BulletML typed structure.
 *						That structure contains the parsed BulletML file ...
 *						... passed as an argument.
 *
 *******************************************************************************************/




/*******************************************************************************************
 *  INCLUDES :
 *******************************************************************************************/

/* System and library related : */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "expat.h"

/* Project related : */
#include "Action.h"
#include "Fire.h"
#include "Bullet.h"
#include "Direction.h"
#include "Speed.h"
#include "Wait.h"
#include "Repeat.h"
#include "ChangeDirection.h"
#include "ChangeSpeed.h"
#include "Accel.h"
#include "Vanish.h"
#include "Direction.h"
#include "Speed.h"
#include "ActionRef.h"
#include "FireRef.h"
#include "BulletRef.h"
#include "Horizontal.h"
#include "Vertical.h"
#include "LesserElements.h"
#include "BulletML.h"







/*******************************************************************************************
 *  CONSTANT definitions :
 *******************************************************************************************/
#define BUFFSIZE        	8192
#define MAX_ELEMENTS_CHAIN 	64







/*******************************************************************************************
 *  TYPE definitions :
 *******************************************************************************************/

/* Some data all the parsing functions needs to keep a hold on : */
typedef struct ParseTempData {
	int			parentElementsIndex;
	void		*parentElementsChain[MAX_ELEMENTS_CHAIN];
} ParseTempData;







/*******************************************************************************************
 *  "PRIVATE" function PROTOTYPES :
 *******************************************************************************************/

/* 'expat' related : */
static void XMLCALL startElement(void *ptmpdata, const char *name, const char **atts);
static void XMLCALL textElement(void *ptmpdata, const XML_Char *s, int len);
static void XMLCALL endElement(void *ptmpdata, const char *name);

/* BulletML related : */





/*******************************************************************************************
 *  FUNCTIONS :
 *******************************************************************************************/

/*******************************************************************************************
 *  NEWBULLETML : creates a new empty BulletML object structure :
 *******************************************************************************************/
BulletML *newBulletML(void) {
	BulletML *bml;

	/* Creating the container structure : */
	bml = (BulletML *)malloc(sizeof(BulletML));


	/* Setting the object's type : */
	bml->otype		= bulletML;


	/* The BulletML object is vertical by default : */
	bml->type		= vertical_bml;


	/* For now, the structure is empty, so : */
	bml->actionsNum	= 0;
	bml->firesNum	= 0;
	bml->bulletsNum	= 0;


	/* Returning the still empty structure : */
	return bml;

}







/*******************************************************************************************
 *  FREEBULLETML : destroys a BulletML object structure :
 *******************************************************************************************/
void freeBulletML(BulletML *bml) {
	int i;		/* All purpose counter */

	/* Releasing all actions : */
	for(i=0; i<bml->actionsNum; i++)
		freeAction(bml->actions[i]);

	/* Releasing all fires : */
	for(i=0; i<bml->firesNum; i++)
		freeFire(bml->fires[i]);

	/* Releasing all bullets : */
	for(i=0; i<bml->bulletsNum; i++)
		freeBullet(bml->bullets[i]);

	/* Releasing the BulletML object : */
	free(bml);
}







/*******************************************************************************************
 *  SETBULLETMLTYPE 	: sets the BulletML objects vertical or horizontal type
 *	ADDBULLETMLACTION	: adds a Action object to the BulletML object
 *	ADDBULLETMLFIRE		: adds a Fire object to the BulletML object
 *	ADDBULLETMLBULLET	: adds a Bullet object to the BulletML object
 *******************************************************************************************/
void setBulletMLType(BulletML *bml, BMLType t) {
	bml->type = t;
}

void addBulletMLAction(BulletML *bml, Action *a) {
	bml->actions[bml->actionsNum++] = a;
}

void addBulletMLFire(BulletML *bml, Fire *f) {
	bml->fires[bml->firesNum++] = f;
}

void addBulletMLBullet(BulletML *bml, Bullet *b) {
	bml->bullets[bml->bulletsNum++] = b;
}







/*******************************************************************************************
 *  PARSEBULLETMLFILE : parses a bulletML file and stores it in a BulletML Structure.
 *******************************************************************************************/
BulletML *parseBulletMLFile(const char *file_name) {

	/* For handling the file : */
	FILE			*f;

	/* For the parser : */
	XML_Parser 		parser;
	ParseTempData	*ptmpdata;
	char			buf[BUFFSIZE];
	int				len;
	int				done;

	/* Returning a pointer to a BulletML object : */
	BulletML		*bml;



	/* OPENING the BulletML XML file : *****************************************************/
	f = fopen(file_name, "r");
	if(f == NULL) {
		printf("Couldn't oopen file %s.\nAborting.", file_name);
		return NULL;
	}


	/* CREATING the PARSER and setting it up : *********************************************/
	parser = XML_ParserCreate(NULL);
	if(!parser) {
		printf("Could not allocate memory for parser\nAborting.\n");
		return NULL;
	}

	ptmpdata = (ParseTempData *)malloc(sizeof(ParseTempData));	/* Setting the temporary... */ 
	ptmpdata->parentElementsIndex 		= 0;					/* ... data structure.      */

	XML_SetUserData(parser, ptmpdata);

	XML_SetElementHandler(parser, startElement, endElement);	/* Handling opening ...   */
																/* ... and closing tags.  */
	XML_SetCharacterDataHandler(parser, textElement);			/* Handling in-tage text. */


	/* PARSING the file : ******************************************************************/
	do {
		len = (int)fread(buf, 1, BUFFSIZE, f);
		done = feof(f);
		if (XML_Parse(parser, buf, len, done) == XML_STATUS_ERROR) {
			fprintf(stderr, "%s at line %d\n",
				XML_ErrorString(XML_GetErrorCode(parser)),
				(int)XML_GetCurrentLineNumber(parser));
		return NULL;
		}
	} while (!done);

	bml = ptmpdata->parentElementsChain[0];						/* Keeping a hold on the ...  */
																/* ... root object, which ... */
																/* ... the BulletML object.   */


	/* CLEARING the parser away : **********************************************************/
	XML_ParserFree(parser);
	free(ptmpdata);


	/* RETURNING the final BulletML object structured : ************************************/
	return bml;

}







/*******************************************************************************************
 *  STARTELEMENT : what to do when an opening tag is found ?
 *******************************************************************************************/
static void XMLCALL startElement(void *ptmpdata, const char *name, const char **atts) {

	int 			i = 0;		/* All purpose counter.					*/

	void 			*ne;		/* Pointer to a newly created element.	*/

	ParseTempData	*ptd;		/* Short for the parseTempData struct.	*/
	BulletML 		*rbml;		/* Short for root BulletML object.		*/
	void			**pec;		/* Short for parentElementsChain.		*/
	Object			*parent;	/* The parent object.					*/



	/* Preparing the short cuts : */
	ptd		= (ParseTempData *)ptmpdata;
	rbml	= (BulletML *)ptd->parentElementsChain[0];
	pec		= ptd->parentElementsChain;
	parent	= (Object *)ptd->parentElementsChain[ptd->parentElementsIndex];



	/***************************************************************************************/
	/* Checking every opening tag found :                                                  */
	/***************************************************************************************/

	/***************************************************************************************/
	/* HIGHER ELEMENTS :                                                                   */
	/***************************************************************************************/

	/* What to do if we find a BULLETML opening tag ? **************************************/
	if (!strcmp(name, "bulletml")) {

		/* Creating the new BulletML object : */
		ne = newBulletML();

		/* What is this BulletML object type ? */
		while(strcmp(atts[i], "type") != 0)
			i+=2;

		if(strcmp(atts[i+1], "horizontal") == 0)
			setBulletMLType(ne, horizontal_bml);
		else
			setBulletMLType(ne, vertical_bml);

		/* Adding the object to the parent chain : */
		pec[ptd->parentElementsIndex] = ne;

	}




	/* What do we do if we find an ACTION opening tag ? ************************************/
	if (!strcmp(name, "action")) {

		ptd->parentElementsIndex++;

		ne = newAction();							/* Creating a new Action object.      */

		/* Setting the label if necessary : */
		if(atts[1] != NULL)
			setActionLabel(ne, (char *)atts[1]);

		addBulletMLAction(rbml, ne);				/* Adding the object to the root ...  */
													/* ... BulletML object's actions list.*/

		switch(parent->otype) {
			case bullet:
				addBulletAction((Bullet *)parent, ne);
				break;
			case action:
				pushActionElement((Action *)parent, ne);
				break;
			case repeat:
				setRepeatAction((Repeat *)parent, ne);
				break;
			default:
				break;
		}

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */
	}




	/* What do we do if we find a FIRE opening tag ? ***************************************/
	if (!strcmp(name, "fire")) {

		ptd->parentElementsIndex++;

		ne = newFire();								/* Creating a new Fire object.        */

		/* Setting the label if necessary : */
		if(atts[1] != NULL)
			setFireLabel(ne, (char *)atts[1]);

		addBulletMLFire(rbml, ne);					/* Adding the object to the root ...  */
													/* ... BulletML object's fires list.  */

		if(parent->otype == action) {
			pushActionElement((Action *)parent, ne);
		}

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */
	}




	/* What do we do if we find a BULLET tag ? *********************************************/
	if (!strcmp(name, "bullet")) {

		ptd->parentElementsIndex++;

		ne = newBullet();							/* Creating a new Bullet object.      */

		/* Setting the label if necessary : */
		if(atts[1] != NULL)
			setBulletLabel(ne, (char *)atts[1]);

		addBulletMLBullet(rbml, ne);				/* Adding the object to the root ...  */
													/* ... BulletML object's bullets list.*/

		if(parent->otype == fire) {
			setFireBullet((Fire *)parent, ne);
		}

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */
	}




	/***************************************************************************************/
	/* ACTION ELEMENTS :                                                                   */
	/***************************************************************************************/

	/* What do we do if we find a WAIT tag ? ***********************************************/
	if (!strcmp(name, "wait")) {

		ptd->parentElementsIndex++;

		ne = newWait();								/* Creating a new Wait object.        */

		/* Pushing the object on the parent Action's element list : */
		pushActionElement((Action *)parent, ne);

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */
	}




	/* What do we do if we find a REPEAT tag ? *********************************************/
	if (!strcmp(name, "repeat")) {

		ptd->parentElementsIndex++;

		ne = newRepeat();							/* Creating a new Repeat object.      */

		/* Pushing the object on the parent Action's element list : */
		pushActionElement((Action *)parent, ne);

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */
	}




	/* What do we do if we find a CHANGEDIRECTION tag ? ************************************/
	if (!strcmp(name, "changeDirection")) {

		ptd->parentElementsIndex++;

		ne = newChangeDirection();					/* Creating a new Direction object.   */

		/* Pushing the object on the parent Action's element list : */
		pushActionElement((Action *)parent, ne);

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */
	}




	/* What do we do if we find a CHANGESPEED tag ? ****************************************/
	if (!strcmp(name, "changeSpeed")) {

		ptd->parentElementsIndex++;

		ne = newChangeSpeed();						/* Creating a new Speed object.       */

		/* Pushing the object on the parent Action's element list : */
		pushActionElement((Action *)parent, ne);

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */
	}




	/* What do we do if we find a ACCEL tag ? **********************************************/
	if (!strcmp(name, "accel")) {

		ptd->parentElementsIndex++;

		ne = newAccel();							/* Creating a new Accel object.       */

		/* Pushing the object on the parent Action's element list : */
		pushActionElement((Action *)parent, ne);

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */
	}




	/* What do we do if we find a VANISH tag ? *********************************************/
	if (!strcmp(name, "vanish")) {

		ptd->parentElementsIndex++;

		ne = newVanish();							/* Creating a new Vanish object.      */

		/* Pushing the object on the parent Action's element list : */
		pushActionElement((Action *)parent, ne);

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */
	}




	/***************************************************************************************/
	/* REFERENCE ELEMENTS :                                                                */
	/***************************************************************************************/

	/* What do we do if we find an ACTIONREF tag ? *****************************************/
	if(!strcmp(name, "actionRef")) {

		ptd->parentElementsIndex++;

		ne = newActionRef();						/* Creating a new ActionRef object */

		/* Setting the label : */
		setActionRefLabel(ne, (char *)atts[1]);

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */

	}




	/* What do we do if we find an FIREREF tag ? *******************************************/
	if(!strcmp(name, "fireRef")) {

		ptd->parentElementsIndex++;

		ne = newFireRef();							/* Creating a new FireRef object.     */

		/* Setting the label : */
		setFireRefLabel(ne, (char *)atts[1]);

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */

	}




	/* What do we do if we find an BULLETREF tag ? *****************************************/
	if(!strcmp(name, "bulletRef")) {

		ptd->parentElementsIndex++;

		ne = newBulletRef();						/* Creating a new BulletRef object.   */

		/* Setting the label : */
		setBulletRefLabel(ne, (char *)atts[1]);

		pec[ptd->parentElementsIndex] = ne;			/* Adding the object to the chain ... */
													/* ... of parents.                    */

	}




	/***************************************************************************************/
	/* MOVEMENT ELEMENTS :                                                                 */
	/***************************************************************************************/

	/* What do we do if we find a DIRECTION tag ? ******************************************/
	if (!strcmp(name, "direction")) {

		ptd->parentElementsIndex++;

		ne = newDirection();						/* Creating a new Direction object.   */

		/* Setting the Direction object's type : */
		if(strcmp(atts[1], "absolute") == 0)
			setDirectionType(ne, absolute_dir);
		else if(strcmp(atts[1], "relative") == 0)
			setDirectionType(ne, relative_dir);
		else if(strcmp(atts[1], "sequence") == 0)
			setDirectionType(ne, sequence_dir);
		else
			setDirectionType(ne, aim_dir);

		/* Assigning the Direction object to the parent Bullet, Fire or ... */
		/* ... ChangeDirection object :                                     */
		switch(parent->otype) {
			case fire:
				setFireDirection((Fire *)parent, ne);
				break;
			case bullet:
				setBulletDirection((Bullet *)parent, ne);
				break;
			case changeDirection:
				setChangeDirectionDirection((ChangeDirection *)parent, ne);
				break;
			default:
				break;
		}

		pec[ptd->parentElementsIndex] = ne;		/* Adding the object to the chain ... */
												/* ... of parents.                    */
	}




	/* What do we do if we find a SPEED tag ? **********************************************/
	if (!strcmp(name, "speed")) {

		ptd->parentElementsIndex++;

		ne = newSpeed();					/* Creating a new Speed object.       */

		/* Setting the Speed object's type : */
		if(strcmp(atts[1], "relative") == 0)
			setSpeedType(ne, relative_spd);
		else if(strcmp(atts[1], "sequence") == 0)
			setSpeedType(ne, sequence_spd);
		else
			setSpeedType(ne, absolute_spd);

		/* Assigning the Speed object to the parent Bullet, Fire or ... */
		/* ... ChangeDirection object :                                 */
		switch(parent->otype) {
			case fire:
				setFireSpeed((Fire *)parent, ne);
				break;
			case bullet:
				setBulletSpeed((Bullet *)parent, ne);
				break;
			case changeSpeed:
				setChangeSpeedSpeed((ChangeSpeed *)parent, ne);
				break;
			default:
				break;
		}

		pec[ptd->parentElementsIndex] = ne;		/* Adding the object to the chain ... */
												/* ... of parents.                    */
	}




	/***************************************************************************************/
	/* LESSER ELEMENTS :                                                                   */
	/***************************************************************************************/

	/* What do we do if we find a HORIZONTAL tag ? *****************************************/
	if (!strcmp(name, "horizontal")) {

		ptd->parentElementsIndex++;

		ne = newHorizontal();					/* Creating a new Horizontal object.  */

		/* Setting the Horizontal object's type : */
		if(strcmp(atts[1], "relative") == 0)
			setHorizontalType(ne, relative_hrt);
		else if(strcmp(atts[1], "sequence") == 0)
			setHorizontalType(ne, sequence_hrt);
		else
			setHorizontalType(ne, absolute_hrt);

		/* Assigning the Horizontal object to the parent Accel object : */
		setAccelHorizontal((Accel *)parent, ne);

		pec[ptd->parentElementsIndex] = ne;		/* Adding the object to the chain ... */
												/* ... of parents.                    */
	}




	/* What do we do if we find a VERTICAL tag ? *******************************************/
	if (!strcmp(name, "vertical")) {

		ptd->parentElementsIndex++;

		ne = newVertical();					/* Creating a new Vertical object.        */

		/* Setting the Vertical object's type : */
		if(strcmp(atts[1], "relative") == 0)
			setVerticalType(ne, relative_vrt);
		else if(strcmp(atts[1], "sequence") == 0)
			setVerticalType(ne, sequence_vrt);
		else
			setVerticalType(ne, absolute_vrt);

		/* Assigning the Vertical object to the parent Accel object : */
		setAccelHorizontal((Accel *)parent, ne);

		pec[ptd->parentElementsIndex] = ne;		/* Adding the object to the chain ... */
												/* ... of parents.                    */
	}




	/* What do we do if we find a PARAM tag ? *******************************************/
	if (!strcmp(name, "param")) {

		ptd->parentElementsIndex++;

		ne = newParam();						/* Creating a new Param object.       */

		pec[ptd->parentElementsIndex] = ne;		/* Adding the object to the chain ... */
												/* ... of parents.                    */
	}




	/* What do we do if we find a TIMES tag ? *******************************************/
	if (!strcmp(name, "times")) {

		ptd->parentElementsIndex++;

		ne = newTimes();						/* Creating a new Times object.       */

		pec[ptd->parentElementsIndex] = ne;		/* Adding the object to the chain ... */
												/* ... of parents.                    */
	}




	/* What do we do if we find a TERM tag ? ********************************************/
	if (!strcmp(name, "term")) {

		ptd->parentElementsIndex++;

		ne = newTerm();							/* Creating a new Term object.        */

		pec[ptd->parentElementsIndex] = ne;		/* Adding the object to the chain ... */
												/* ... of parents.                    */
	}

}







/*******************************************************************************************
 *  TEXTELEMENT : what to do with the text inside an element ?
 *******************************************************************************************/
static void XMLCALL textElement(void *ptmpdata, const XML_Char *s, int len) {

	ParseTempData	*ptd;		/* Short for the parseTempData struct.	*/
	void			**pec;		/* Short for parentElementsChain.		*/
	void			*co;		/* Current object.                      */
	Object			*parent;

	char			*expr;


	/* Preparing the short cuts : */
	ptd		= (ParseTempData *)ptmpdata;
	pec		= ptd->parentElementsChain;

	co		= ptd->parentElementsChain[ptd->parentElementsIndex];
	parent	= (Object *)ptd->parentElementsChain[ptd->parentElementsIndex-1];


	/* Preparing the expression string : */
	expr	= malloc(MAX_EXPRESSION_STR_LEN);
	strncpy(expr, s, len);


	/* Putting the expression string in the right place : */
	switch(((Object *)co)->otype) {

		/* Normal objects : */
		case waiting:
			setWaitFrames((Wait *)co, expr);
			break;
		case direction:
			setDirectionValue((Direction *)co, expr);
			break;
		case speed:
			setSpeedValue((Speed *)co, expr);
			break;

		/* Objects using a Param object : */
		case param:
			switch(parent->otype) {
				case actionRef:
					pushActionRefParam((ActionRef *)parent, expr);
					break;
				case fireRef:
					pushFireRefParam((FireRef *)parent, expr);
					break;
				case bulletRef:
					pushBulletRefParam((BulletRef *)parent, expr);
					break;
				default:
					break;
			}
			break;

		/* Objects using a Term object : */
		case term:
			switch(parent->otype) {
				case changeDirection:
					setChangeDirectionTerm((ChangeDirection *)parent, expr);
					break;
				case changeSpeed:
					setChangeSpeedTerm((ChangeSpeed *)parent, expr);
					break;
				case accel:
					setAccelTerm((Accel *)parent, expr);
					break;
				default:
					break;
			}
			break;

		/* Objects using the Times object : */
		case times:
			setRepeatTimes((Repeat *)parent, expr);
			break;

		/* Default : */
		default:
			break;
	}

}







/*******************************************************************************************
 *  ENDELEMENT : what to do when we find a closing tag ?
 *******************************************************************************************/
static void XMLCALL endElement(void *ptmpdata, const char *name) {

	ParseTempData	*ptd;		/* Short for the parseTempData struct	*/
	void			**pec;		/* Short for parentElementsChain		*/



	/* Preparing the shortcuts : */
	ptd		= (ParseTempData *)ptmpdata;
	pec		= ((ParseTempData *)ptmpdata)->parentElementsChain;



	/***************************************************************************************/
	/* Checking every closing tag found :                                                  */
	/***************************************************************************************/

	/* What to do if we find a BULLETML closing tag ? **************************************/
	if (!strcmp(name, "bulletml")) {
		// Do nothing...
	}

	/* What to do if we find a ACTION closing tag ? ****************************************/
	if (!strcmp(name, "action")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a FIRE closing tag ? ******************************************/
	if (!strcmp(name, "fire")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a BULLET closing tag ? ****************************************/
	if (!strcmp(name, "bullet")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a WAIT closing tag ? ******************************************/
	if (!strcmp(name, "wait")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a REPEAT closing tag ? ****************************************/
	if (!strcmp(name, "repeat")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a CHANGEDIRECTION closing tag ? *******************************/
	if (!strcmp(name, "changeDirection")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a CHANGESPEED closing tag ? ***********************************/
	if (!strcmp(name, "changeSpeed")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a ACCEL closing tag ? *****************************************/
	if (!strcmp(name, "accel")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a VANISH closing tag ? ****************************************/
	if (!strcmp(name, "vanish")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a ACTIONREF closing tag ? *************************************/
	if (!strcmp(name, "actionRef")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a FIREREF closing tag ? ***************************************/
	if (!strcmp(name, "fireRef")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a BULLETREF closing tag ? *************************************/
	if (!strcmp(name, "bulletRef")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a DIRECTION closing tag ? *************************************/
	if (!strcmp(name, "direction")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a SPEED closing tag ? *****************************************/
	if (!strcmp(name, "speed")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a HORIZONTAL closing tag ? ************************************/
	if (!strcmp(name, "horizontal")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a VERTICAL closing tag ? **************************************/
	if (!strcmp(name, "vertical")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a PARAM closing tag ? *****************************************/
	if (!strcmp(name, "param")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a TIMES closing tag ? *****************************************/
	if (!strcmp(name, "times")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}

	/* What to do if we find a TERM closing tag ? ******************************************/
	if (!strcmp(name, "term")) {
		pec[ptd->parentElementsIndex] = NULL;
		ptd->parentElementsIndex--;
	}
	
}
